package com.zengjianjun.es.demo.highlevelclient.param;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.index.reindex.BulkByScrollResponse;

import java.util.HashMap;
import java.util.Map;

/**
 * es-根据条件更新-Param
 * <p>
 * <hr/>
 *
 * <p>根据条件更新 elasticsearch 中的数据，相当于 <i>查询mySQL时</i> 的 update table set bField=xxx where aField = xxx; 多用于给es数据打标签的场景。</p>
 * <p>
 * 其中：
 * <ol>
 *      <li> {@link EsUpdateByQueryParam#queryParam} 相当于以上sql中，where之后的 <i>aField = xxx;</i>，详细解释请参照 {@link EsQueryParam} 类中的注释部分。
 *          <p>例如：</p>
 *              <pre class="code">
 *                  EsGaidQuery.EsGaidQueryBuilder builder = EsGaidQuery.builder();
 *                  if (StringUtils.isNotEmpty(param.getSource())) {
 *                      builder.source(FieldValue.<String>builder().value(param.getSource()).conditionOption(ConditionOption.equal).build());
 *                  }
 *                  if (StringUtils.isNotEmpty(param.getCountry())) {
 *                      builder.country(FieldValue.<String>builder().value(param.getCountry()).conditionOption(ConditionOption.equal).build());
 *                  }
 *                  if (StringUtils.isNotEmpty(param.getEvent())) {
 *                      builder.event(FieldValue.<String>builder().value(param.getEvent()).conditionOption(ConditionOption.equal).build());
 *                  }
 *                  if (StringUtils.isNotEmpty(param.getPlatform())) {
 *                      builder.platform(FieldValue.<String>builder().value(param.getPlatform()).conditionOption(ConditionOption.equal).build());
 *                  }
 *                  if (StringUtils.isNotEmpty(param.getCat())) {
 *                      builder.cat(FieldValue.<String>builder().value(param.getCat()).conditionOption(ConditionOption.equal).build());
 *                  }
 *                  if (StringUtils.isNotEmpty(param.getSubCat())) {
 *                      builder.subCat(FieldValue.<String>builder().value(param.getSubCat()).conditionOption(ConditionOption.equal).build());
 *                  }
 *                  if (param.getIncludedPkg() != null) {
 *                      builder.includePkg(FieldValue.<List<String>>builder().value(param.getIncludedPkg()).conditionOption(ConditionOption.or).build());
 *                  }
 *                  if (param.getExcludedPkg() != null) {
 *                      builder.excludePkg(FieldValue.<List<String>>builder().value(param.getExcludedPkg()).conditionOption(ConditionOption.or).build());
 *                  }
 *                  if (StringUtils.isNotEmpty(param.getStartCreateTime()) && StringUtils.isNotEmpty(param.getEndCreateTime())) {
 *                      builder.timeOption(TimeOption.builder()
 *                              .field("createTime")
 *                              .interval(0)
 *                              .startTime(param.getStartCreateTime())
 *                              .endTime(param.getEndCreateTime())
 *                              .build());
 *                  }
 *                  return builder.build();
 *              </pre>
 *
 *      <li> {@link EsUpdateByQueryParam#includes} 表明了查询后-修改前，这之间的查询返回字段有哪些，只让要修改的字段返回，理论上可以提高 elasticsearch 的处理速度。
 *      <li> updateFieldAndVal 表明需要修改的字段与值。
 *          <p>例如：</p>
 *              <pre class="code">
 *                  Map&lt;String, Object&gt; updateFieldAndVal = new HashMap&lt;&gt;();
 *                  updateFieldAndVal.put("cat", cat);
 *                  updateFieldAndVal.put("subCat", subCat);
 *              </pre>
 *
 *      <li> {@link EsUpdateByQueryParam#script} 使得支持脚本更新。脚本中声明了的 params['abc'] 部分，abc这个字段和值，也必须在 EsQueryParam 中做出声明。
 *          <p>例如：</p>
 *              <pre class="code">
 *                  EsUpdateByQueryParam esUpdateByQueryParam = EsUpdateByQueryParam.builder()
 *                          .index(EsConstant.INDEX_GAID)
 *                          .queryParam(esQueryParam)
 *                          .includes(new String[]{"cat", "subCat"})
 *                          .updateFieldAndVal(updateFieldAndVal)
 *                          .script("ctx._source.cat = params['cat']; ctx._source.subCat = params['subCat'];")
 *                          .listener(GaidListener.build())
 *                          .build();
 *              </pre>
 *              其中的 TagAddScript.SCRIPT_CAT 内容为：
 *              <pre class="code">
 *                  public static final String SCRIPT_CAT = "ctx._source.cat = params['cat']; ctx._source.subCat = params['subCat'];";
 *              </pre>
 *
 *      <li> {@link EsUpdateByQueryParam#listener} 为监听器，在异步处理时，必须有监听器对异步处理的返回结果做监听处理。
 *          <p>例如：</p>
 *              <pre class="code">
 *                  public class GaidListener implements ActionListener<BulkByScrollResponse> {
 *                        public void onResponse(BulkByScrollResponse response) {
 *                            log.info("GaidListener, took = {} , total = {} , updated = {} , reasonCancelled = {}",
 *                                    response.getTook(),
 *                                    response.getTotal(),
 *                                    response.getUpdated(),
 *                                    response.getReasonCancelled());
 *                        }
 *                        public void onFailure(Exception e) {
 *                            log.error("GaidListener Exception : {}", e.getLocalizedMessage());
 *                        }
 *                    }
 *              </pre>
 * </ol>
 * <hr/>
 *
 * @author zengjianjun
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EsUpdateByQueryParam {

    /**
     * 索引名称
     */
    private String index;
    /**
     * 查询条件
     */
    private EsQueryParam queryParam;
    /**
     * 查询后，在elasticsearch内部所返回的字段列表，理论上可以提高es的处理速度。
     */
    private String[] includes = {};
    /**
     * 需要更新的字段与值。
     */
    private Map<String, Object> updateFieldAndVal;
    /**
     * 支持脚本更新
     */
    private String script;
    /**
     * 异步模式的监听器。
     */
    private ActionListener<BulkByScrollResponse> listener;

    public static EsUpdateByQueryParamBuilder builder() {
        return new EsUpdateByQueryParamBuilder();
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class EsUpdateByQueryParamBuilder {
        /**
         * 索引名称
         */
        private String index;
        /**
         * 查询条件
         */
        private EsQueryParam queryParam;
        /**
         * 查询后，在elasticsearch内部所返回的字段列表，理论上可以提高es的处理速度。
         */
        private String[] includes = {};
        /**
         * 需要更新的字段与值。
         */
        private Map<String, Object> updateFieldAndVal;
        /**
         * 支持脚本更新
         */
        private String script;
        /**
         * 异步模式的监听器。
         */
        private ActionListener<BulkByScrollResponse> listener;

        public EsUpdateByQueryParamBuilder index(String index) {
            this.index = index;
            return this;
        }

        public EsUpdateByQueryParamBuilder queryParam(EsQueryParam queryParam) {
            this.queryParam = queryParam;
            return this;
        }

        public EsUpdateByQueryParamBuilder includes(String[] includes) {
            this.includes = includes;
            return this;
        }

        public EsUpdateByQueryParamBuilder updateFieldAndVal(Map<String, Object> updateFieldAndVal) {
            this.updateFieldAndVal = updateFieldAndVal;
            return this;
        }

        public EsUpdateByQueryParamBuilder fieldAndVal(String field, String value) {
            if (this.updateFieldAndVal == null) {
                updateFieldAndVal = new HashMap<>();
            }
            this.updateFieldAndVal.put(field, value);
            return this;
        }

        public EsUpdateByQueryParamBuilder script(String script) {
            this.script = script;
            return this;
        }

        public EsUpdateByQueryParamBuilder listener(ActionListener<BulkByScrollResponse> listener) {
            this.listener = listener;
            return this;
        }

        public EsUpdateByQueryParam build() {
            return new EsUpdateByQueryParam(index, queryParam, includes, updateFieldAndVal, script, listener);
        }
    }
}
